<script setup lang="ts">
  import styles from "./captcha.module.scss";
  import IconRefresh from "./icons/IconRefresh.vue";
  import SliderBar from "./SliderBar.vue";
  import { CaptchaStatus, CaptchaSideIcon, CaptchaMode } from "./models";

  const apis = {
    behaviorVerify: "/api/behaviorVerify", // 行为验证
    refresh: "/captcha/gen", // 刷新
  };

  const props = defineProps({
    // 应用ID
    appId: {
      type: String,
      required: true,
    },
    theme: String, // 默认、圆角
    // 随机串
    nonce: String,
    // 无感验证
    sence: Boolean,
    // 拖动图标
    sideIcon: {
      type: Number,
      // default: CaptchaSideIcon.Airplane,
      default: CaptchaSideIcon.Arrow,
    },
    mode: {
      default: CaptchaMode.Embed,
      type: String,
    },
  });

  const rid = ref("");
  const token = ref("");
  const bgSrc = ref("/assets/captcha_image_1.jpg");
  const puzzleSrc = ref("");

  const rootElm = ref<HTMLDivElement>(null);
  const sliderBar = ref<typeof SliderBar>();

  const btnOffsetX = computed(() => {
    if (sliderBar.value && rootElm.value) {
      return sliderBar.value?.offsetRatio * (rootElm.value.clientWidth - 40); // 40是拼图的宽度
    } else {
      return 0;
    }
  });
  const status = computed(() => sliderBar.value?.status);
  const showPopup = ref(false);

  const computedRootClasses = computed(() => {
    const classes = [styles.captcha];
    if (props.theme) {
      classes.push(`puzzle-${props.theme}`);
    }
    if (props.mode) {
      classes.push(`puzzle-${props.mode}`);
    }
    switch (status.value) {
      case CaptchaStatus.Active:
        classes.push("active");
        break;
      case CaptchaStatus.Loading:
        classes.push("loading");
        break;
      case CaptchaStatus.Validing:
        classes.push("validing");
        break;
      case CaptchaStatus.Success:
        classes.push("success");
        break;
      case CaptchaStatus.Fail:
        classes.push("fail");
        break;
      case CaptchaStatus.Complete:
        classes.push("complete");
        break;
    }
    return classes;
  });

  const computedPuzzleStyle = computed(() => {
    if (btnOffsetX.value > 0) {
      if (status.value !== CaptchaStatus.Loading && status.value !== CaptchaStatus.None) {
        return {
          transform: `translateX(${btnOffsetX.value}px)`,
        };
      }
    } else {
      return {};
    }
  });

  // refreshCaptcha();

  function onAnywhereClickExceptPopup(e) {
    if (e.target.closest(".puzzle--image")) {
      return;
    }
    showPopup.value = false;
    document.body.removeEventListener("mousedown", onAnywhereClickExceptPopup);
  }
  function onSliderBarActive() {
    if (props.mode === CaptchaMode.Popup) {
      showPopup.value = true;
      document.body.addEventListener("mousedown", onAnywhereClickExceptPopup, false);
    }
  }
  function onSliderBarSuccess() {
    showPopup.value = false;
    document.body.removeEventListener("mousedown", onAnywhereClickExceptPopup);
  }
  function refreshCaptcha() {
    sliderBar.value.status = CaptchaStatus.Loading;
  }
</script>

<template>
  <div ref="rootElm"
       class="puzzle"
       :class="computedRootClasses"
       @contextmenu.prevent>
    <div class="puzzle--image"
         :class="{
           show:props.mode === CaptchaMode.Popup && showPopup
         }">
      <img v-if="bgSrc"
           class="puzzle--image--remote"
           :src="bgSrc"/>
      <canvas class="puzzle--image--original"></canvas>
      <img v-if="puzzleSrc"
           class="puzzle--image--puzzle"
           :src="puzzleSrc"
           :style="computedPuzzleStyle"/>
      <div class="puzzle--image--toolbar">
        <div class="toolbar-item--refresh"
             @click.stop="refreshCaptcha">
          <icon-refresh :class="{
            [styles.animRotate]:status === CaptchaStatus.Loading
          }"></icon-refresh>
        </div>
      </div>
    </div>

    <slider-bar ref="sliderBar"
                :appId="appId"
                :nonce="nonce"
                placeholder="向右拖动完成拼图"
                :sence="sence"
                :sideIcon="sideIcon"
                :theme="props.theme"
                @active="onSliderBarActive"
                @success="onSliderBarSuccess" >
    </slider-bar>
  </div>
</template>

<style lang="scss" scoped>
  // 所有的svg 图标都会影响上一层元素事件的触发，所以需要禁用
  svg {
    pointer-events: none;
  }

  .puzzle {
    --puzzle-image-radius: 5.25px;
    --puzzle-toolbar-height: 40px;
    --puzzle-bg-image: url("/assets/captcha_image_1.jpg");

    &--image {
      position: relative;
      width: 100%;
      // height: 168px;
      min-height: 133px;
      border-radius: var(--puzzle-image-radius);
      margin-bottom: 8px;
      background-color: #eee;
      background-size: cover;
      background-position: center;
      background-image: var(--puzzle-bg-image);
      overflow: hidden;
      font-size: 0;

      &--remote {
        width: 100%;
        height: 100%;
      }
      &--original {
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
        z-index: 0;
      }

      &--puzzle {
        position: absolute;
        left: 0;
        top: 0;
        z-index: 1;
        height: 100%;
        width: auto;
        background-color: rgba(255, 255, 255, 0.3);
        transition: all 0.75s ease-out;
      }
      &--toolbar {
        position: absolute;
        right: 0;
        top: 0;
        z-index: 2;
        border-bottom-left-radius: var(--puzzle-image-radius);
        background-color: rgba(0, 0, 0, 0.4);
        color: #fff;

        > [class^="toolbar-item"] {
          line-height: 1;
          font-size: 20px;
          padding: 5px;
          display: inline-flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
        }
        &:hover {
          color: #eee;
        }
      }
    }

    &.active &--image--puzzle,
    &.validing &--image--puzzle {
      transition: none;
    }

    &-popup &--image {
      position: absolute;
      bottom: 100%;
      display: none;
      box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.6);

      &.show {
        display: block;
      }
    }
  }
</style>
